from enum import Enum
import random
import os
import sys
import signal
from mainUi import Ui_main_window
from signUpDlg import SignUpDlg
from PyQt5 import QtMultimedia, QtGui
from PyQt5.QtWidgets import QStatusBar, QLabel, QHBoxLayout, QGridLayout,\
    QMainWindow, QHeaderView, QFileDialog, QWidget, QLineEdit, QPushButton, QMessageBox
from PyQt5.QtGui import QPixmap, QColor, QIntValidator, QPalette, QFont
from PyQt5.QtCore import Qt, QTimer, QMargins, QSettings, QUrl
from word import CWord
from server import Server
import time
import datetime
import res_rc

limit_list = (10,20,100)


class Mode(Enum):
    MODE_TEST = 0,
    MODE_REAL = 1


class Operate(Enum):
    OP_ADD = 0,
    OP_SUB = 1
    OP_MUL = 2
    OP_DIV = 3


class MainDlg(QMainWindow, Ui_main_window):
    def __init__(self):
        super(QMainWindow, self).__init__()
        self.setupUi(self)
        self.setWindowTitle(self.tr('Math Generator'))
        self.show_table_widget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.show_table_widget.verticalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.show_table_widget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.show_table_widget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.show_table_widget.setFocusPolicy(Qt.NoFocus)
        self._reg = QSettings("HKEY_CURRENT_USER\\Software\\LinkTest", QSettings.NativeFormat)
        # right_mp3_file = f'{os.path.dirname(os.path.abspath(__file__))}\\right.mp3'
        right_mp3_file = os.path.dirname(os.path.realpath(sys.executable)) + '\\right.mp3'
        # QMessageBox.information(self, 'notice', right_mp3_file)
        mp3_right = QUrl.fromLocalFile(right_mp3_file)
        self.mp3_right_player = QtMultimedia.QMediaPlayer()
        self.mp3_right_player.setMedia(QtMultimedia.QMediaContent(mp3_right))
        self.mp3_right_player.setVolume(100)
        wrong_mp3_file = os.path.dirname(os.path.realpath(sys.executable)) + '\\wrong.mp3'
        # f"{os.path.dirname(os.path.abspath(__file__))}\\wrong.mp3"
        mp3_wrong = QUrl.fromLocalFile(wrong_mp3_file)
        self.mp3_wrong_player = QtMultimedia.QMediaPlayer()
        self.mp3_wrong_player.setMedia(QtMultimedia.QMediaContent(mp3_wrong))
        self.mp3_right_player.setVolume(100)
        self.limit = 10
        self.exit = False
        self.time_start = 0
        self.time_end = 0
        self.group = []
        self.last_first = 0
        self.last_second = 0
        self.user_info = None
        self._link_mode_label = QLabel(self.tr('Offline Mode'))
        self.advertising_board = QLineEdit()
        self.contact_board = QLineEdit()
        self.statusBar = None
        self.sign_up_dlg = SignUpDlg()
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap(":/img/login"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.sign_up_dlg.setWindowIcon(icon)
        self.ft = QFont()
        self.pa = QPalette()
        self._add_new_user_btn = QPushButton(self.tr('Add New User'))
        self._server = Server()
        self.create_statusbar()
        # self.pb_flash.clicked.connect(self._flash_clicked)
        self.load_cfg()
        self.mode_combobox.currentIndexChanged[int].connect(self._mode_changed)
        self.limit_combobox.currentIndexChanged[int].connect(self._limit_changed)
        self.counter_mode_combobox.currentIndexChanged[int].connect(self._count_mode_changed)
        self.select_add.clicked.connect(self._method_switch)
        self.select_sub.clicked.connect(self._method_switch)
        self.select_mul.clicked.connect(self._method_switch)
        self.select_div.clicked.connect(self._method_switch)
        self.pb_submit.clicked.connect(self._submit_clicked)
        self.pb_start.clicked.connect(self._start_clicked)
        self.pb_export_to_word.setToolTip(self.tr('This feature could be used under 3 times for free!'))
        self.pb_export_to_word.resize(self.pb_export_to_word.sizeHint())
        self.pb_export_to_word.clicked.connect(self._export_to_word)
        self._add_new_user_btn.clicked.connect(self._add_new_user_slot)
        self.sign_up_dlg.saved[str].connect(self._add_new_user_success)
        self.time_setting_spinbox.valueChanged.connect(self._counter_spinbox_value_changed)
        self.load_user_info()
        # if self._server.connect_to_server():
        # timer = threading.Timer(0.5, self._update_adv)
        # timer.start()
        self._count_timer = QTimer()
        self._count_timer.timeout.connect(self._counter)
        self.timer = QTimer()
        self.timer.timeout.connect(self._update_adv)
        self.timer.setInterval(15*1000)
        self.timer.start()

    def closeEvent(self, event):
        reply = QMessageBox.question(self, self.tr('Warning'),
                                     self.tr('Please make sure the program will be exitted!'),
                                     QMessageBox.Yes, QMessageBox.No)
        if reply == QMessageBox.Yes:
            # self._server.close()
            self.exit = True
            event.accept()
            # time.sleep(0.2)
            # os.kill(os.getpid(), signal.SIGKILL)
        else:
            event.ignore()

    def create_statusbar(self):
        self.statusBar = QStatusBar()
        self.setStatusBar(self.statusBar)
        self.setStyleSheet("QStatusBar::item{border: 0px}")
        self.advertising_board.setDisabled(False)
        self.advertising_board.setMinimumWidth(650)
        self.advertising_board.setReadOnly(True)
        self.advertising_board.setMinimumHeight(30)
        self.advertising_board.setAlignment(Qt.AlignHCenter)
        self.advertising_board.setText(self.tr('pearls_of_wisdom'))
        self.ft.setPointSize(13)
        self.advertising_board.setFont(self.ft)
        # self.advertising_board.setStyleSheet("background-color:rgba(0,255,255,255)")
        self.statusBar.addPermanentWidget(self.advertising_board)

        self.statusBar.addPermanentWidget(self.contact_board)
        self.contact_board.setMinimumWidth(280)
        self.contact_board.setReadOnly(True)
        self.contact_board.setMinimumHeight(30)
        self.contact_board.setAlignment(Qt.AlignHCenter)
        self.contact_board.setStyleSheet("QLineEdit{color:blue}")
        self.contact_board.setText(self.tr('Teacher Zhang'))
        # self.contact_board.setStyleSheet("background-color:rgba(0,255,255,255)")
        self.statusBar.addPermanentWidget(QLabel(self.tr('Score:')))
        self.score_label = QLabel('888')
        self.rank_label = QLabel('100')
        self.score_label.setFont(self.ft)
        self.statusBar.addPermanentWidget(self.score_label)
        self.statusBar.addPermanentWidget(QLabel(self.tr('|')))
        self.statusBar.addPermanentWidget(QLabel(self.tr('Rank:')))
        self.rank_label.setFont(self.ft)
        self.statusBar.addPermanentWidget(self.rank_label)
        self._link_mode_label.setFont(self.ft)

    def load_user_info(self):
        if (conn_stat := self._server.connect_to_server()) is False:
            self._server.close()
            conn_stat = self._server.connect_to_server()
        if conn_stat:
            if (user_id := self.get_user_id()) != '00000000000' and (info := self._server.get_user_info(user_id)) is not None:
                self.user_info = info
                self.statusBar.addWidget(self._link_mode_label)
                self._link_mode_label.setText(self.tr('Welcome') + info.user_name + self.tr('come back!'))
                if info.feature_export_to_word == 1:
                    self.set_export_to_word_use_count(9999999)
            else:
                self.statusBar.addWidget(self._add_new_user_btn)
            self._server.close()
        else:
            self.statusBar.addWidget(self._link_mode_label)

    def reload_user_info(self):
        self.statusBar.removeWidget(self._add_new_user_btn)
        user_id = self.get_user_id()
        if user_id != '00000000000':
            if self._server.connect_to_server():
                info = self._server.get_user_info(user_id)
                if info is not None:
                    self.user_info = info
                    self.statusBar.addWidget(self._link_mode_label)
                    self._link_mode_label.setText(self.tr('Welcome') + info.user_name + self.tr('come back!'))
                self._server.close()

    def load_cfg(self):
        method_type = self.get_type_index()
        if method_type == 0:
            self.select_add.setChecked(True)
        elif method_type == 1:
            self.select_sub.setChecked(True)
        elif method_type == 2:
            self.select_mul.setChecked(True)
        elif method_type == 3:
            self.select_div.setChecked(True)

        index = self.get_mode_index()
        self.mode_combobox.setCurrentIndex(index)
        self.set_mode_index(index)
        if index == 1:
            self.pb_start.setDisabled(True)
            self.pb_submit.setDisabled(True)
            self.counter_mode_combobox.setDisabled(True)
            self.time_setting_spinbox.setDisabled(True)
        elif index == 0:
            self.pb_start.setDisabled(False)
            self.pb_submit.setDisabled(False)
            self.counter_mode_combobox.setDisabled(False)
            self.time_setting_spinbox.setDisabled(False)

        index = self.get_limit_index()
        self.limit_combobox.setCurrentIndex(index)
        self.counter_mode_combobox.setCurrentIndex(self.get_count_mode_index())
        self.time_setting_spinbox.setDisabled(self.counter_mode_combobox.currentIndex() == 0)
        self.time_setting_spinbox.setValue(self.get_count_time())
        self.pb_submit.setDisabled(True)
        self.generate()

    def _check_access_right(self):
        count = self.get_export_to_word_use_count()
        if count > 0:
            self.set_export_to_word_use_count(count - 1)
            return True
        return False

    def _insert_all(self):
        for i in range(4):
            self.show_table_widget.insertColumn(i)

        for i in range(26):
            self.show_table_widget.insertRow(i)

    def _clear_all(self):
        # clear table column
        self.show_table_widget.clear()
        col_count = self.show_table_widget.columnCount()
        for i in range(col_count):
            self.show_table_widget.removeColumn(0)

        row_count = self.show_table_widget.rowCount()
        for i in range(row_count):
            self.show_table_widget.removeRow(0)

    def generate(self):
        self.group = []
        result_list = []
        self._clear_all()
        self._insert_all()
        self.limit = limit_list[self.limit_combobox.currentIndex()]
        operate_type, operate_label = self.get_operate()
        validator = QIntValidator(0, self.limit*10)
        self.ft.setPointSize(14)
        self.pa.setColor(QPalette.WindowText, Qt.blue)
        result_count = 0
        for col in range(4):
            # col_layout = QGridLayout()
            # self.container.addLayout(col_layout)
            col_view = QLabel(self.tr('Column ') + str(col + 1))
            col_view.setPalette(self.pa)
            col_view.setAlignment(Qt.AlignCenter)
            self.show_table_widget.setCellWidget(0, col, col_view)
            col_list = []
            for index in range(1, 26):
                item_hash = {}
                first_num, second_num = self.generate_number(operate_type)
                item_hash['first_num'] = first_num
                item_hash['first_num_label'] = QLabel(str(first_num))
                item_hash['first_num_label'].setFont(self.ft)
                item_hash['operate'] = operate_type
                item_hash['operate_label'] = QLabel(operate_label)
                item_hash['operate_label_val'] = operate_label
                # item_hash['operate_label'].setFont(self.ft)
                item_hash['second_num'] = second_num
                item_hash['second_num_label'] = QLabel(str(second_num))
                item_hash['second_num_label'].setFont(self.ft)
                item_hash['eq_label'] = QLabel(' = ')
                # item_hash['eq_label'].setFont(self.ft)
                item_hash['result_editor'] = QLineEdit()
                # item_hash['result_editor'].setAlignment(Qt.AlignHCenter)
                result_list.append(item_hash['result_editor'])
                if result_count > 0:
                    QWidget.setTabOrder(result_list[result_count - 1], result_list[result_count])
                else:
                    item_hash['result_editor'].setFocus()
                if 0 == self.get_mode_index():
                    item_hash['result_editor'].setDisabled(True)
                result_count = result_count + 1
                item_hash['result_editor'].setMinimumHeight(27)
                # item_hash['result_editor'].setMaximumWidth(60)
                item_hash['result_editor'].setValidator(validator)
                # self.spin_list[col][index]
                # item_hash['result_editor'].setMaximumWidth(70)
                # item_hash['result_editor'].setStyleSheet("QSpinBox{border:1px solid #242424;}")
                # item_hash['result_editor'].setStyleSheet("QSpinBox::up-button,QSpinBox::down-button{width:0px;}")
                item_hash['result_editor'].editingFinished.connect(self._slot)
                item_hash['mark_label'] = QLabel()
                # layout = QHBoxLayout()
                # item_hash['mark_label'].setMinimumWidth(30)
                widget = QWidget()
                widget.setContentsMargins(QMargins(0, 0, 0, 0))
                cell_layout = QGridLayout(widget)
                cell_layout.setSpacing(6)
                cell_layout.setContentsMargins(QMargins(5, 0, 0, 0))
                cell_layout.addWidget(item_hash['first_num_label'], 0, 0, 1, 1, Qt.AlignLeft)
                cell_layout.addWidget(item_hash['operate_label'], 0, 1, 1, 1,  Qt.AlignLeft)
                cell_layout.addWidget(item_hash['second_num_label'], 0, 2,  1, 1,  Qt.AlignLeft)
                cell_layout.addWidget(item_hash['eq_label'], 0, 3,  1, 1,  Qt.AlignLeft)
                cell_layout.addWidget(item_hash['result_editor'], 0, 4, 1, 5,  Qt.AlignLeft)
                cell_layout.addWidget(item_hash['mark_label'], 0, 5,  1, 4, Qt.AlignRight)
                widget.setFocusPolicy(Qt.NoFocus)
                self.show_table_widget.setCellWidget(index, col, widget)
                col_list.append(item_hash)

            self.group.append(col_list)
            result_list[0].setFocus()

    def get_operate(self):
        if self.select_add.isChecked():
            self.set_type_index(Operate.OP_ADD.value)
            return Operate.OP_ADD, '+'
        elif self.select_sub.isChecked():
            self.set_type_index(Operate.OP_SUB.value)
            return Operate.OP_SUB, '-'
        elif self.select_mul.isChecked():
            self.set_type_index(Operate.OP_MUL.value)
            return Operate.OP_MUL, 'x'
        elif self.select_div.isChecked():
            self.set_type_index(Operate.OP_DIV.value)
            return Operate.OP_DIV, '÷'

    def generate_number(self,operate_type):
        first = random.randint(0, self.limit - 1)
        second = random.randint(0, self.limit - 1)
        if operate_type is Operate.OP_ADD or operate_type is Operate.OP_MUL:
            while True:
                if first != self.last_first and second != self.last_first:
                    break
                first = random.randint(0, self.limit - 1)
                second = random.randint(0, self.limit - 1)
        elif operate_type is Operate.OP_SUB:
            while True:
                if first > second and first != self.last_first and second != self.last_first:
                    break
                first = random.randint(0, self.limit - 1)
                second = random.randint(0, self.limit - 1)
        elif operate_type is Operate.OP_DIV:
            while True:
                if second !=0 and first % second == 0 and first != second and first != self.last_first and second != self.last_second:
                    break
                first = random.randint(0, self.limit - 1)
                second = random.randint(0, self.limit - 1)
        self.last_first = first
        self.last_second = second
        return first,second

    def _slot(self):
        if self.mode_combobox.currentIndex() == Mode.MODE_REAL.value:
            val = int(self.sender().text())
            print(val)
            for col in range(len(self.group)):
                for row in range(len(self.group[col])):
                    if self.sender() == self.group[col][row]['result_editor']:
                        print("Column: {0}, Row: {1}".format(col, row))
                        first_num = self.group[col][row]['first_num']
                        second_num = self.group[col][row]['second_num']
                        type = self.group[col][row]['operate']
                        ret = self.calc(first_num, second_num,type)
                        label = self.group[col][row]['mark_label']
                        if ret == val:
                            self.set_right(label)
                        else:
                            self.set_wrong(label)
                        print("Input: {0}, Result: {1}".format(val, ret))

    def set_right(self, label):
        label.setAlignment(Qt.AlignCenter)
        pix = QPixmap(":/img/correct")
        new_pix = pix.scaled(24, 24, Qt.KeepAspectRatio)
        label.setScaledContents(False)
        label.setPixmap(new_pix)
        self.mp3_right_player.play()

    def set_wrong(self, label):
        label.setAlignment(Qt.AlignCenter)
        pix = QPixmap(":/img/illegal")
        new_pix = pix.scaled(24, 24, Qt.KeepAspectRatio)
        label.setScaledContents(False)
        label.setPixmap(new_pix)
        self.mp3_wrong_player.play()

    @staticmethod
    def set_label_null(label):
        label.setAlignment(Qt.AlignCenter)
        label.setPixmap(QPixmap(""))

    def _mode_changed(self, index):
        self.set_mode_index(index)
        if index == 1:
            self.pb_start.setDisabled(True)
            self.pb_submit.setDisabled(True)
            self.counter_mode_combobox.setDisabled(True)
            self.time_setting_spinbox.setDisabled(True)
        elif index == 0:
            self.counter_mode_combobox.setDisabled(False)
            self.pb_start.setDisabled(False)
            self.pb_submit.setDisabled(True)
            if self.counter_mode_combobox.currentIndex() == 0:
                self.time_setting_spinbox.setDisabled(True)
            else:
                self.time_setting_spinbox.setDisabled(False)

        self.generate()

    def _count_mode_changed(self, index):
        self.set_count_mode_index(index)
        self.time_setting_spinbox.setDisabled(index == 0)

    def _limit_changed(self, index):
        self.set_limit_index(index)
        self.generate()

    def _start_clicked(self):
        self.pb_submit.setDisabled(False)
        self.pb_start.setDisabled(True)
        col_num = len(self.group)
        row_num = len(self.group[0])
        for col in range(col_num):
            for row in range(row_num):
                self.group[col][row]['result_editor'].setDisabled(False)
                self.group[col][row]['result_editor'].setText('')
                self.set_label_null(self.group[col][row]['mark_label'])
        self.group[0][0]['result_editor'].setFocus()
        if self.get_count_mode_index() == 1:
            self._count_timer.setInterval(1000*self.time_setting_spinbox.value())
            self._count_timer.start()
        self.time_start = datetime.datetime.now()

    def _submit_clicked(self):
        self.pb_submit.setDisabled(True)
        self.pb_start.setDisabled(False)
        correct_number = 0
        incorrect_number = 0
        col_num = len(self.group)
        row_num = len(self.group[0])
        for col in range(col_num):
            for row in range(row_num):
                first = self.group[col][row]['first_num']
                second = self.group[col][row]['second_num']
                operate = self.group[col][row]['operate']
                ret = self.calc(first, second, operate)
                text = self.group[col][row]['result_editor'].text()
                result = int()
                if len(text) == 0 or ret != int(text):
                    self.set_wrong(self.group[col][row]['mark_label'])
                    incorrect_number = incorrect_number + 1
                else:
                    self.set_right(self.group[col][row]['mark_label'])
                    correct_number = correct_number + 1
        text = self.tr('Correct: ') + str(correct_number) + '  ' + self.tr('Error: ') + \
               str(incorrect_number) + '  ' + self.tr('score: ') + \
               str(correct_number) + self.tr(' point')
        # 0 是考试模式
        if self.get_mode_index() == 0:
            self.time_end = datetime.datetime.now()
            time_span = (self.time_end - self.time_start).seconds
            text = text + self.tr('  Total Time') + str(time_span) + self.tr(' Seconds')
        QMessageBox.information(self, self.tr('Notice'), text)

    def _export_to_word(self):
        if self._check_access_right() is False:
            QMessageBox.information(self, self.tr('Notice'), self.tr('This feature could be used under 3 times for free!'))
            return
        directory = QFileDialog.getExistingDirectory(self, 'Select Dir',"./")

        if len(directory) > 0:
            self._word = CWord()
            limit_text = self.limit_combobox.currentText()
            method_text = ''
            if self.select_add.isChecked():
                method_text = self.select_add.text()
            elif self.select_sub.isChecked():
                method_text = self.select_sub.text()
            elif self.select_mul.isChecked():
                method_text = self.select_mul.text()
            elif self.select_div.isChecked():
                method_text = self.select_div.text()

            title = limit_text + method_text
            full_path = directory + '\\' + title + time.strftime('-%Y-%m-%d-%H-%M-%S', time.localtime(time.time()))
            self._word.new_docx(title, full_path, self.group)

    def _add_new_user_slot(self):
        self.sign_up_dlg.exec_()

    def _add_new_user_success(self, id):
        self.set_user_id(id)
        self.reload_user_info()

    def _counter_spinbox_value_changed(self, val):
        self.set_count_time(val)

    def _method_switch(self, arg):
        if self.sender() == self.select_add:
            self.set_type_index(Operate.OP_ADD.value)
        elif self.sender() == self.select_sub:
            self.set_type_index(Operate.OP_SUB.value)
        elif self.sender() == self.select_mul:
            self.set_type_index(Operate.OP_MUL.value)
        elif self.sender() == self.select_div:
            self.set_type_index(Operate.OP_DIV.value)
        self.generate()

    def calc(self, first, second, type):
        if type == Operate.OP_ADD:
            return self.add(first,second)
        elif type == Operate.OP_SUB:
            return self.sub(first,second)
        elif type == Operate.OP_MUL:
            return self.mul(first,second)
        elif type == Operate.OP_DIV:
            return self.div(first,second)

    def add(self, first, second):
        return first + second

    def sub(self, first, second):
        return first - second

    def mul(self, first, second):
        return first * second

    def div(self, first, second):
        return int(first / second)

    def set_limit_index(self, index):
        self._reg.setValue('limit_combobox_index', index)

    def get_limit_index(self):
        return self._reg.value('limit_combobox_index', 0)

    def set_count_mode_index(self, index):
        self._reg.setValue('count_mode_combobox_index', index)

    def get_count_mode_index(self):
        return self._reg.value('count_mode_combobox_index', 0)

    def set_mode_index(self, index):
        self._reg.setValue('mode_combobox_index', index)

    def get_mode_index(self):
        return self._reg.value('mode_combobox_index', 0)

    def set_type_index(self, index):
        self._reg.setValue('type_index', index)

    def get_type_index(self):
        return self._reg.value('type_index', 0)

    def set_count_time(self, count_time):
        self._reg.setValue('count_time', count_time)

    def get_count_time(self):
        return self._reg.value('count_time', 0)

    def set_user_id(self, id: str):
        self._reg.setValue('user_id', id)

    def get_user_id(self) -> str:
        return self._reg.value('user_id', '00000000000')

    def set_export_to_word_use_count(self, count):
        self._reg.setValue('export_to_word_use_count', count)

    def get_export_to_word_use_count(self):
        return self._reg.value('export_to_word_use_count', 3)

    def _update_adv(self):
        if self.exit is True:
            return
        if self._server.connect_to_server():
            content, publisher, contact = self._server.get_advertise_info(random.randint(0, 99))
            if content is not None:
                self.advertising_board.setText(content)
                self.contact_board.setText(contact)
            # threading.Timer(10, self._update_adv).start()
            self._server.close()

    def _counter(self):
        self._count_timer.stop()
        col_num = len(self.group)
        row_num = len(self.group[0])
        for col in range(col_num):
            for row in range(row_num):
                self.group[col][row]['result_editor'].setDisabled(True)
        self._submit_clicked()